﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Diagnostics;
using System.Data.Objects;
using System.Linq.Expressions;
using AgileKiwi.QueryCache;

namespace ClassLibrary1
{
    // todo: sort out better timing tests.  While tests tests having timings in them, they don't really 
    // function as timing tests because (a) they don't repeat stuff and (b) the queries are so simple that 
    // compilation won't necessarily make a huge difference anyway.

    [TestFixture]
    public class Tests
    {
        [TestFixtureSetUp]
        public void WarmUp()
        {
            // take the hit of EF's once-per-app domain initialization here, so that it doesn't mess up the result
            // of the first test
            using (var context = new NorthwindEntities())
            {
                var customers = (from c in context.Customers
                                 where c.City == "London"
                                 select c).ToList();
            }
        }

        #region Comparisons to other ways to query

        [Test]
        public void CanQuery()
        {
            var s = new Stopwatch();
            using (var context = new NorthwindEntities())
            {
                s.Start();
                var customers = (from c in context.Customers
                                where c.City == "London"
                                select c).ToList();
                Console.WriteLine(s.ElapsedMilliseconds); 
            }

            using (var context = new NorthwindEntities())
            {
                s.Restart();
                var customers = (from c in context.Customers
                                where c.City == "London"
                                select c).ToList();
                Console.WriteLine(s.ElapsedMilliseconds);     // should take roughly the same amt of time as first one
            
                //foreach (var c in customers)
                //    Console.WriteLine(c.Company_Name);
               // Assert.That(customers.Count() > 0);
            }
        }

        Func<NorthwindEntities, string, string, IQueryable<Customer>> _customerQuery = 
            CompiledQuery.Compile<NorthwindEntities, string, string, IQueryable<Customer>>(
                (ctx, name, postcode) =>
                    from c in ctx.Customers
                    where c.City == name
                    select c);

        [Test]
        public void CanQueryWithNormalCompilation()
        {
            var s = new Stopwatch();

            using (var context = new NorthwindEntities())
            {
                s.Start();
                var customers1 = _customerQuery(context, "London", "WX3 6FW").ToList();
                Console.WriteLine(s.ElapsedMilliseconds);
            }

            using (var context = new NorthwindEntities())
            {
                s.Restart();
                var customers2 = _customerQuery(context, "London", "WX3 6FW").ToList();
                Console.WriteLine(s.ElapsedMilliseconds);
                //foreach (var c in customers2)
                //    Console.WriteLine(c.Company_Name);
            }

        }
        #endregion

        [Test]
        public void CanQuery_WithCache_UsingOneParameter()
        {
            var s = new Stopwatch();
            using (var context = new NorthwindEntities())
            {
                s.Start();
                var customers1 = GetCustomers(context, "Tokyo").ToList();
                Console.WriteLine(s.ElapsedMilliseconds);
            }

            using(var context = new NorthwindEntities())    // use another context, so that timings prove that caching worked
            {
                s.Restart();
                var customers2 = GetCustomers(context, "London").ToList();
                Console.WriteLine(s.ElapsedMilliseconds);
                Assert.That(customers2.Count() > 0);
            }
        }

        private IEnumerable<Customer> GetCustomers(NorthwindEntities context, string cityName)
        {
            return context.Pass(new { cityName })
                          .ToCompiledQuery<Customer>(() => (ctx, p) =>
                    from c in ctx.Customers
                    where c.City == p.cityName
                    select c);
        }

        [Test]
        public void CanQuery_WithCache_UsingMultipleParameters()
        {
            using (var context = new NorthwindEntities())
            {
                var customers = GetCustomers(context, "London", "WX3 6FW").ToList();
                Assert.That(customers.Count() == 1);

                foreach (var c in customers)
                    Console.WriteLine(c.Company_Name + " " + c.Postal_Code);
            }
        }

        private IEnumerable<Customer> GetCustomers(NorthwindEntities context, string cityName, string postcode)
        {
            return context.Pass(new { cityName, postcode })
                          .ToCompiledQuery<Customer>(() => (ctx, p) =>
                    from c in ctx.Customers
                    where c.City == p.cityName &&
                          c.Postal_Code == p.postcode
                    select c);
        }

        [Test]
        public void Clear()   // just used as a manual-resest for those test runners that don't reload the app domain on re-runs
        {
            QueryCache.Default.Clear();
        }
       


                                                                    
    }
}
